from airtest.core.api import *
import re
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
import time
import os
import cv2
import json
import logging

# 使用已有的logger而不是创建新的
from log_util import logger  # 假设log_util.py在同一目录下


class EasonFansAutoSign:
    def __init__(self, chrome_driver_path=r"C:\Program Files\chromedriver-win64\chromedriver.exe"):
        """初始化自动签到类"""
        # 连接Windows设备
        connect_device("Windows:///")
        self.chrome_driver_path = chrome_driver_path
        self.driver = None

        # 确保captcha文件夹存在，使用绝对路径
        script_dir = os.path.dirname(os.path.abspath(__file__))
        self.captcha_folder = os.path.join(script_dir, "captcha")
        if not os.path.exists(self.captcha_folder):
            os.makedirs(self.captcha_folder)

        # 确保digits文件夹存在（用于验证码识别）
        self.digits_folder = os.path.join(script_dir, "digits")
        if not os.path.exists(self.digits_folder):
            logger.debug(f"警告: {self.digits_folder} 文件夹不存在，请确保数字模板图片已准备")

    def preprocess_for_recognition(self, image):
        """为识别预处理图像"""
        # 转换为灰度图
        if len(image.shape) == 3:
            gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        else:
            gray = image

        # 应用高斯模糊减少噪声
        blurred = cv2.GaussianBlur(gray, (3, 3), 0)
        return blurred

    def recognize_single_digit(self, digit_img):
        """识别单个数字，使用多尺度匹配"""
        best_confidence = 0
        best_digit = 0

        # 尝试匹配0-9，找出最佳匹配
        for digit in range(10):
            template_path = f"digits/{digit}.png"
            if os.path.exists(template_path):
                try:
                    # 读取模板图片并进行预处理
                    template_img = cv2.imread(template_path, cv2.IMREAD_GRAYSCALE)
                    if template_img is not None:
                        # 对模板也进行相同的预处理
                        processed_template = self.preprocess_for_recognition(template_img)

                        # 尝试多种尺寸的模板匹配
                        scales = [0.8, 0.9, 1.0, 1.1, 1.2]
                        max_confidence = 0

                        for scale in scales:
                            try:
                                # 调整模板大小
                                new_width = int(processed_template.shape[1] * scale)
                                new_height = int(processed_template.shape[0] * scale)

                                if new_width > 0 and new_height > 0:
                                    resized_template = cv2.resize(processed_template, (new_width, new_height))

                                # 确保模板不大于目标图像
                                if (resized_template.shape[0] <= digit_img.shape[0] and
                                        resized_template.shape[1] <= digit_img.shape[1]):
                                    # 进行模板匹配
                                    res = cv2.matchTemplate(digit_img, resized_template, cv2.TM_CCOEFF_NORMED)
                                    _, confidence, _, _ = cv2.minMaxLoc(res)
                                    max_confidence = max(max_confidence, confidence)
                            except:
                                continue

                        if max_confidence > best_confidence and max_confidence > 0.35:
                            best_confidence = max_confidence
                            best_digit = digit

                except Exception as e:
                    continue

        return best_digit, best_confidence

    def recognize_captcha_with_templates(self, captcha_path):
        """
        使用模板匹配方式识别验证码
        需要预先准备0-9的数字模板图片，放在digits文件夹中
        """
        # 加载验证码图片
        captcha_img = cv2.imread(captcha_path)

        # 图像预处理
        processed_captcha = self.preprocess_for_recognition(captcha_img)

        result = ""
        height, width = processed_captcha.shape

        # 等宽分割5位数字验证码
        for i in range(5):
            # 计算每个数字区域
            digit_width = width // 5
            left = i * digit_width
            right = (i + 1) * digit_width

            # 添加一些边距避免边界问题
            left = max(0, left + 2)
            right = min(width, right - 2)

            # 裁剪数字区域
            digit_img = processed_captcha[0:height, left:right]

            # 识别单个数字
            digit, confidence = self.recognize_single_digit(digit_img)
            result += str(digit)

            logger.debug(f"第{i + 1}位数字: {digit}, 置信度: {confidence:.2f}")

        return result

    def solve_captcha(self):
        """解决验证码"""
        if "网站防火墙" in self.driver.title:
            logger.debug("当前页面是验证码页面")

            num = 0
            while num < 10:
                try:
                    # 直接截图验证码元素
                    captcha_element = self.driver.find_element(By.XPATH, "//img")
                    captcha_element.screenshot("captcha/captcha.png")
                    logger.debug("已直接保存验证码图片")

                    # 使用模板匹配识别验证码
                    captcha_result = self.recognize_captcha_with_templates("captcha/captcha.png")
                    logger.debug(f"识别到的验证码: {captcha_result}")

                    self.driver.find_element(By.XPATH, "//input[@id='intext']").send_keys(captcha_result)
                    self.driver.find_element(By.XPATH, "//input[@type='submit']").click()
                    time.sleep(1)

                    if "首页" in self.driver.title:
                        logger.debug("验证码验证成功")
                        return True
                    else:
                        num += 1
                        logger.debug(f"验证码验证失败，错误次数{num}次")

                except Exception as e:
                    logger.debug(f"验证码处理出错: {e}")
                    num += 1
            return False
        return True

    def login(self, username, password):
        """登录账户"""
        try:
            self.driver.find_element(By.XPATH, "//a[text()='登录']").click()
            time.sleep(1)
            self.driver.find_element(By.XPATH, "//td[@fwin='login']/div[1]/div[1]//input[@name='username']").send_keys(
                username)
            self.driver.find_element(By.XPATH, "//input[@name='password']").send_keys(password)
            self.driver.find_element(By.XPATH, "//button[@name='loginsubmit']").click()
            time.sleep(3)
            return True
        except Exception as e:
            logger.debug(f"登录时出错: {e}")
            return False

    def get_formhash_from_driver(self):
        """从已登录的浏览器中获取formhash"""
        try:
            # 访问签到页面
            self.driver.get("https://www.easonfans.com/forum/plugin.php?id=dsu_paulsign:sign")
            time.sleep(2)

            # 获取页面源码
            page_source = self.driver.page_source

            # 多种方式提取formhash
            patterns = [
                r'formhash=([a-f0-9]{8})',
                r'name="formhash" value="([a-f0-9]{8})"',
                r'formhash["\']?\s*[:=]\s*["\']?([a-f0-9]{8})'
            ]

            for pattern in patterns:
                match = re.search(pattern, page_source)
                if match:
                    return match.group(1)

            logger.debug("未能从页面中提取formhash")
            return None

        except Exception as e:
            logger.debug(f"从浏览器获取formhash时出错: {e}")
            return None

    def cookies_to_string(self, cookies):
        """将selenium的cookies转换为字符串格式"""
        cookie_str = ""
        for cookie in cookies:
            cookie_str += f"{cookie['name']}={cookie['value']}; "
        return cookie_str.rstrip("; ")

    def save_to_json(self, data, filename="sign_config.json"):
        """保存配置到JSON文件"""
        try:
            with open(filename, 'w', encoding='utf-8') as f:
                json.dump(data, f, ensure_ascii=False, indent=2)
            logger.debug(f"配置已保存到 {filename}")
            return True
        except Exception as e:
            logger.debug(f"保存到JSON文件时出错: {e}")
            return False

    def get_sign_info(self, username="default"):
        """获取签到所需的信息（cookies和formhash）"""
        try:
            # 获取cookies
            cookies = self.driver.get_cookies()
            cookie_string = self.cookies_to_string(cookies)
            logger.debug(f"获取到的Cookie: {cookie_string}")

            # 从已登录的页面获取formhash
            formhash = self.get_formhash_from_driver()
            logger.debug(f"获取到的formhash: {formhash}")

            if formhash:
                # 生成签到所需的完整数据
                header = {
                    "Host": "www.easonfans.com",
                    "Connection": "keep-alive",
                    "Content-Length": "56",
                    "Cache-Control": "max-age=0",
                    "sec-ch-ua": '"Chromium";v="140", "Not=A?Brand";v="24", "Google Chrome";v="140"',
                    "sec-ch-ua-mobile": "?0",
                    "sec-ch-ua-platform": '"Windows"',
                    "Origin": "https://www.easonfans.com",
                    "Content-Type": "application/x-www-form-urlencoded",
                    "Upgrade-Insecure-Requests": "1",
                    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36",
                    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
                    "Sec-Fetch-Site": "same-origin",
                    "Sec-Fetch-Mode": "navigate",
                    "Sec-Fetch-User": "?1",
                    "Sec-Fetch-Dest": "iframe",
                    "Referer": "https://www.easonfans.com/forum/plugin.php?id=dsu_paulsign:sign",
                    "Accept-Encoding": "gzip, deflate, br, zstd",
                    "Accept-Language": "zh-CN,zh;q=0.9",
                    "Cookie": cookie_string
                }
                text = f"formhash={formhash}&qdxq=kx&qdmode=3&todaysay=&fastreply=0"

                sign_info = {
                    "username": username,
                    "header": header,
                    "text": text,
                    "cookie_string": cookie_string,
                    "formhash": formhash,
                    "timestamp": time.strftime("%Y-%m-%d %H:%M:%S")
                }

                logger.debug("签到配置信息获取成功")
                return sign_info
            else:
                logger.debug("未能获取到formhash")
                return None

        except Exception as e:
            logger.debug(f"获取签到信息时出错: {e}")
            return None

    def start_browser(self):
        """启动浏览器"""
        try:
            service = Service(executable_path=self.chrome_driver_path)
            self.driver = webdriver.Chrome(service=service)
            self.driver.get("https://www.easonfans.com/")
            time.sleep(1)
            return True
        except Exception as e:
            logger.debug(f"启动浏览器时出错: {e}")
            return False

    def run(self, username, password):
        """运行完整的自动签到流程"""
        try:
            # 启动浏览器
            if not self.start_browser():
                return False

            # 解决验证码
            if not self.solve_captcha():
                return False

            # 登录
            if not self.login(username, password):
                return False

            # 获取签到信息
            sign_info = self.get_sign_info(username)
            return sign_info

        except Exception as e:
            logger.debug(f"运行过程中出错: {e}")
            return None
        finally:
            if self.driver:
                self.driver.quit()


def save_multiple_accounts(accounts, filename="sign_config.json"):
    """保存多个账号信息到同一个JSON文件"""
    all_accounts = {}

    # 创建自动签到实例
    auto_sign = EasonFansAutoSign()

    # 为每个账号获取信息
    for username, password in accounts:
        logger.debug(f"\n正在处理账号: {username}")
        sign_info = auto_sign.run(username, password)
        if sign_info:
            all_accounts[username] = sign_info
            logger.debug(f"账号 {username} 信息获取成功")
        else:
            logger.debug(f"账号 {username} 信息获取失败")

    # 保存所有账号信息到文件
    try:
        with open(filename, 'w', encoding='utf-8') as f:
            json.dump(all_accounts, f, ensure_ascii=False, indent=2)
        logger.debug(f"\n所有账号配置已保存到 {filename}")
        return True
    except Exception as e:
        logger.debug(f"保存到JSON文件时出错: {e}")
        return False


# 使用示例
if __name__ == "__main__":
    # 定义多个账号
    accounts = [
        ("ChinUp!", "DaoHaoSi7j"),
        ("奕思音乐七", "Qaz727214")
    ]

    # 保存所有账号信息到同一个文件
    success = save_multiple_accounts(accounts, "sign_config.json")

    if success:
        logger.debug("所有账号信息获取并保存成功")

        # 读取并显示保存的信息
        try:
            with open("sign_config.json", 'r', encoding='utf-8') as f:
                all_accounts = json.load(f)

            logger.debug("\n保存的账号信息:")
            for username, info in all_accounts.items():
                logger.debug(f"用户名: {username}")
                logger.debug(f"Formhash: {info['formhash']}")
                logger.debug(f"时间戳: {info['timestamp']}")
                logger.debug("-" * 30)
        except Exception as e:
            logger.debug(f"读取配置文件时出错: {e}")
    else:
        logger.debug("账号信息获取或保存失败")
